from django.contrib import admin
from django.shortcuts import redirect
from django.contrib import messages
from django.template.response import TemplateResponse
from django.urls import path
from django.utils import timezone
from .models import (
    Project,
    Environment,
    ApiInterface,
    TestCase,
    TestReport,
    TestExecution  # 仅导入使用，不注册
)
from django.utils.html import format_html
from django.urls import reverse
import json
from django.utils.safestring import mark_safe
import os
from django.conf import settings

# 注册其他模型
@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
    list_display = ['name', 'description', 'created_time', 'updated_time']
    search_fields = ['name', 'description']
    list_filter = ['created_time', 'updated_time']
    ordering = ['-created_time']


@admin.register(Environment)
class EnvironmentAdmin(admin.ModelAdmin):
    list_display = ['name', 'project', 'base_url', 'description']
    search_fields = ['name', 'base_url']
    list_filter = ['project']
    ordering = ['project', 'name']


@admin.register(ApiInterface)
class ApiInterfaceAdmin(admin.ModelAdmin):
    list_display = ['name', 'project', 'method', 'path', 'description']
    search_fields = ['name', 'path']
    list_filter = ['project', 'method']
    ordering = ['project', 'name']


@admin.register(TestCase)
class TestCaseAdmin(admin.ModelAdmin):
    list_display = ['name', 'interface', 'priority', 'status', 'last_execution_result', 'last_execution_time', 'get_actions']
    search_fields = ['name', 'description']
    list_filter = ['interface__project', 'interface', 'priority', 'status']
    ordering = ['-updated_time']
    list_per_page = 20
    change_list_template = 'admin/api_manager/testcase/change_list.html'

    def changelist_view(self, request, extra_context=None):
        """重写列表视图，添加统计数据"""
        extra_context = extra_context or {}
        # 添加统计数据
        extra_context['active_count'] = TestCase.objects.filter(status='active').count()
        extra_context['inactive_count'] = TestCase.objects.filter(status='inactive').count()
        return super().changelist_view(request, extra_context)

    def get_urls(self):
        urls = super().get_urls()
        custom_urls = [
            path('execute-all/', self.admin_site.admin_view(self.execute_all_view), name='api_manager_testcase_execute_all'),
            path('execute-selected/', self.admin_site.admin_view(self.execute_selected_view), name='api_manager_testcase_execute_selected'),
        ]
        return custom_urls + urls

    def last_execution_result(self, obj):
        """获取最后一次执行结果"""
        last_execution = TestExecution.objects.filter(test_case=obj).order_by('-start_time').first()
        if not last_execution:
            return '-'
        
        result_class = ''
        if last_execution.result == 'pass':
            result_class = 'success'
        elif last_execution.result == 'fail':
            result_class = 'warning'
        elif last_execution.result == 'error':
            result_class = 'error'
        else:
            result_class = 'info'
            
        return format_html('<span class="{}">{}</span>', 
                         result_class,
                         last_execution.get_result_display() if last_execution.result else '-')
    last_execution_result.short_description = '最近执行结果'

    def last_execution_time(self, obj):
        """获取最后一次执行时间"""
        last_execution = TestExecution.objects.filter(test_case=obj).order_by('-start_time').first()
        if not last_execution or not last_execution.start_time:
            return '-'
        return last_execution.start_time.strftime('%Y-%m-%d %H:%M:%S')
    last_execution_time.short_description = '最近执行时间'

    def get_actions(self, obj):
        """生成操作按钮"""
        if not obj or not isinstance(obj, TestCase):
            return ''
        return format_html(
            '<a class="button" href="{}">执行</a>',
            reverse('admin:api_manager_testcase_execute_selected') + f'?ids={obj.id}'
        )
    get_actions.short_description = '操作'

    def execute_all_view(self, request):
        """执行所有测试用例"""
        try:
            # 获取所有启用状态的测试用例
            test_cases = TestCase.objects.filter(status='active')
            if not test_cases.exists():
                messages.error(request, '没有可执行的测试用例')
                return redirect('..')

            # 获取环境信息
            if request.method == 'POST':
                environment_id = request.POST.get('environment')
                if not environment_id:
                    messages.error(request, '请选择执行环境')
                    return redirect('..')

                environment = Environment.objects.get(id=environment_id)
                
                # 执行测试用例
                executions = []
                from .core.test_engine import BatchTestExecutionEngine
                engine = BatchTestExecutionEngine(environment=environment, executor=request.user)
                executions, _ = engine.execute_cases(list(test_cases))

                # 生成测试报告
                if executions:
                    project = test_cases.first().interface.project
                    report = TestReport.objects.create(
                        name=f"{project.name}-测试报告-{timezone.now().strftime('%Y%m%d%H%M%S')}",
                        project=project,
                        creator=request.user,
                        start_time=min(exe.start_time for exe in executions if exe.start_time),
                        end_time=max(exe.end_time for exe in executions if exe.end_time),
                        total_cases=len(executions),
                        passed_cases=sum(1 for exe in executions if exe.result == 'pass'),
                        failed_cases=sum(1 for exe in executions if exe.result == 'fail'),
                        error_cases=sum(1 for exe in executions if exe.result == 'error'),
                        skipped_cases=sum(1 for exe in executions if exe.result == 'skip'),
                    )
                    report.executions.set(executions)
                    
                    messages.success(request, f'测试执行完成，已生成测试报告: {report.name}')
                    return redirect('admin:api_manager_testreport_change', report.pk)

            # 显示环境选择页面
            environments = Environment.objects.filter(
                project__apiinterface__testcase__in=test_cases
            ).distinct()
            
            context = {
                'title': '选择执行环境 (执行全部用例)',
                'environments': environments,
                'opts': self.model._meta,
            }
            return TemplateResponse(request, 'admin/api_manager/testcase/execute.html', context)

        except Exception as e:
            messages.error(request, f'执行测试时发生错误: {str(e)}')
            return redirect('admin:api_manager_testcase_changelist')

    def execute_selected_view(self, request):
        """执行选中的测试用例"""
        try:
            # 获取选中的测试用例
            selected_ids = request.GET.get('ids', '').split(',') if request.GET.get('ids') else request.POST.getlist('_selected_action')
            if not selected_ids:
                messages.error(request, '请选择要执行的测试用例')
                return redirect('..')

            test_cases = TestCase.objects.filter(id__in=selected_ids)
            if not test_cases.exists():
                messages.error(request, '未找到要执行的测试用例')
                return redirect('..')

            # 获取环境信息
            if request.method == 'POST':
                environment_id = request.POST.get('environment')
                if not environment_id:
                    messages.error(request, '请选择执行环境')
                    return redirect('..')

                environment = Environment.objects.get(id=environment_id)
                
                # 执行测试用例
                from .core.test_engine import BatchTestExecutionEngine
                engine = BatchTestExecutionEngine(environment=environment, executor=request.user)
                executions, _ = engine.execute_cases(list(test_cases))

                # 生成测试报告
                if executions:
                    project = test_cases.first().interface.project
                    report = TestReport.objects.create(
                        name=f"{project.name}-测试报告-{timezone.now().strftime('%Y%m%d%H%M%S')}",
                        project=project,
                        creator=request.user,
                        start_time=min(exe.start_time for exe in executions if exe.start_time),
                        end_time=max(exe.end_time for exe in executions if exe.end_time),
                        total_cases=len(executions),
                        passed_cases=sum(1 for exe in executions if exe.result == 'pass'),
                        failed_cases=sum(1 for exe in executions if exe.result == 'fail'),
                        error_cases=sum(1 for exe in executions if exe.result == 'error'),
                        skipped_cases=sum(1 for exe in executions if exe.result == 'skip'),
                    )
                    report.executions.set(executions)
                    
                    messages.success(request, f'测试执行完成，已生成测试报告: {report.name}')
                    return redirect('admin:api_manager_testreport_change', report.pk)

            # 显示环境选择页面
            environments = Environment.objects.filter(
                project__apiinterface__testcase__in=test_cases
            ).distinct()
            
            context = {
                'title': '选择执行环境',
                'environments': environments,
                'opts': self.model._meta,
                'selected': ','.join(map(str, selected_ids)),
            }
            return TemplateResponse(request, 'admin/api_manager/testcase/execute.html', context)

        except Exception as e:
            messages.error(request, f'执行测试时发生错误: {str(e)}')
            return redirect('admin:api_manager_testcase_changelist')

    def save_model(self, request, obj, form, change):
        if not change:  # 如果是新建对象
            obj.creator = request.user
        super().save_model(request, obj, form, change)

    fieldsets = [
        ('基本信息', {
            'fields': ['name', 'interface', 'description', 'priority', 'status']
        }),
        ('测试配置', {
            'fields': ['test_data', 'expected_results'],
            'description': '请按照以下格式填写测试数据和预期结果（JSON格式）：<br>'
                         '测试数据示例：{"params": {"id": 1}, "headers": {"token": "xxx"}}<br>'
                         '预期结果示例：{"status_code": 200, "response": {"code": 0, "message": "success"}}'
        }),
        ('依赖配置', {
            'fields': ['dependencies', 'pre_conditions', 'post_conditions'],
            'classes': ['collapse']
        })
    ]

    class Media:
        css = {
            'all': ('admin/css/custom.css',)
        }
        js = ('admin/js/jquery.init.js', 'admin/js/core.js', 'admin/js/json-format.js')


@admin.register(TestReport)
class TestReportAdmin(admin.ModelAdmin):
    list_display = ['name', 'project', 'total_cases', 'passed_cases', 'failed_cases', 'error_cases', 'pass_rate', 'created_time', 'get_allure_report']
    search_fields = ['name']
    list_filter = ['project', 'created_time']
    ordering = ['-created_time']
    readonly_fields = ['name', 'project', 'total_cases', 'passed_cases', 'failed_cases', 'error_cases', 'skipped_cases', 'pass_rate', 'created_time', 'get_execution_details', 'get_allure_report']
    change_list_template = 'admin/api_manager/testreport/change_list.html'

    fieldsets = [
        ('报告信息', {
            'fields': ['name', 'project', 'created_time']
        }),
        ('统计信息', {
            'fields': ['total_cases', 'passed_cases', 'failed_cases', 'error_cases', 'skipped_cases', 'pass_rate']
        }),
        ('执行详情', {
            'fields': ['get_execution_details'],
            'classes': ['collapse']  # 可折叠的部分
        })
    ]

    def get_execution_details(self, obj):
        """获取执行详情的HTML展示"""
        html = []
        for execution in obj.executions.all():
            # 获取完整的请求URL
            base_url = execution.environment.base_url if execution.environment else ''
            path = execution.test_case.interface.path if execution.test_case.interface else ''
            full_url = base_url.rstrip('/') + '/' + path.lstrip('/')

            # 构建HTML
            html.append(f'<div style="margin-bottom: 20px; border: 1px solid #ddd; padding: 10px;">')
            html.append(f'<h3 style="color: #333;">用例：{execution.test_case.name}</h3>')
            html.append(f'<p><strong>接口：</strong>{execution.test_case.interface.name}</p>')
            html.append(f'<p><strong>请求方法：</strong>{execution.test_case.interface.method}</p>')
            html.append(f'<p><strong>请求URL：</strong>{full_url}</p>')
            html.append(f'<p><strong>执行结果：</strong>{execution.get_result_display()}</p>')
            html.append(f'<p><strong>开始时间：</strong>{execution.start_time.strftime("%Y-%m-%d %H:%M:%S") if execution.start_time else "-"}</p>')
            html.append(f'<p><strong>结束时间：</strong>{execution.end_time.strftime("%Y-%m-%d %H:%M:%S") if execution.end_time else "-"}</p>')
            
            # 请求信息
            html.append('<div style="margin: 10px 0;">')
            html.append('<h4 style="color: #666;">请求信息：</h4>')
            if execution.request_data:
                if 'headers' in execution.request_data:
                    html.append('<p><strong>请求头：</strong></p>')
                    html.append(f'<pre style="background: #f5f5f5; padding: 10px;">{json.dumps(execution.request_data["headers"], indent=2, ensure_ascii=False)}</pre>')
                if 'params' in execution.request_data:
                    html.append('<p><strong>URL参数：</strong></p>')
                    html.append(f'<pre style="background: #f5f5f5; padding: 10px;">{json.dumps(execution.request_data["params"], indent=2, ensure_ascii=False)}</pre>')
                if 'data' in execution.request_data:
                    html.append('<p><strong>表单数据：</strong></p>')
                    html.append(f'<pre style="background: #f5f5f5; padding: 10px;">{json.dumps(execution.request_data["data"], indent=2, ensure_ascii=False)}</pre>')
                if 'json' in execution.request_data:
                    html.append('<p><strong>JSON数据：</strong></p>')
                    html.append(f'<pre style="background: #f5f5f5; padding: 10px;">{json.dumps(execution.request_data["json"], indent=2, ensure_ascii=False)}</pre>')
            html.append('</div>')
            
            # 响应信息
            html.append('<div style="margin: 10px 0;">')
            html.append('<h4 style="color: #666;">响应信息：</h4>')
            if execution.response_data:
                html.append(f'<p><strong>状态码：</strong>{execution.response_data.get("status_code", "-")}</p>')
                if 'headers' in execution.response_data:
                    html.append('<p><strong>响应头：</strong></p>')
                    html.append(f'<pre style="background: #f5f5f5; padding: 10px;">{json.dumps(execution.response_data["headers"], indent=2, ensure_ascii=False)}</pre>')
                if 'body' in execution.response_data:
                    html.append('<p><strong>响应体：</strong></p>')
                    html.append(f'<pre style="background: #f5f5f5; padding: 10px;">{json.dumps(execution.response_data["body"], indent=2, ensure_ascii=False)}</pre>')
            html.append('</div>')
            
            # 错误信息
            if execution.error_message:
                html.append('<div style="margin: 10px 0;">')
                html.append('<h4 style="color: #666;">错误信息：</h4>')
                html.append(f'<pre style="background: #f5f5f5; padding: 10px; color: #d9534f;">{execution.error_message}</pre>')
                html.append('</div>')
            
            html.append('</div>')

        return mark_safe(''.join(html))
    get_execution_details.short_description = '执行详情'

    def get_allure_report(self, obj):
        """获取 Allure 报告链接"""
        return format_html(
            '<a href="/allure/{}" class="button" target="_blank">查看 Allure 报告</a>',
            obj.id
        )
    get_allure_report.short_description = 'Allure 报告'

    def get_urls(self):
        urls = super().get_urls()
        custom_urls = [
            path('export-pdf/<int:pk>/', self.admin_site.admin_view(self.export_pdf_view), name='api_manager_testreport_export_pdf'),
            path('allure/<int:pk>/',
                 self.admin_site.admin_view(self.view_allure_report),
                 name='api_manager_testreport_allure'),
        ]
        return custom_urls + urls

    def export_pdf_view(self, request, pk):
        try:
            report = self.model.objects.get(pk=pk)
            from .core.report_generator import ReportGenerator
            
            # 准备报告数据
            report_data = {
                'name': report.name,
                'project_name': report.project.name,
                'created_time': report.created_time.strftime('%Y-%m-%d %H:%M:%S'),
                'total_cases': self.total_cases(report),
                'passed_cases': self.passed_cases(report),
                'failed_cases': self.failed_cases(report),
                'error_cases': self.error_cases(report),
                'skipped_cases': self.skipped_cases(report),
                'pass_rate': self.pass_rate(report),
                'executions': []
            }

            # 添加执行详情
            for execution in report.executions.all():
                # 获取完整的请求URL
                base_url = execution.environment.base_url if execution.environment else ''
                path = execution.test_case.interface.path if execution.test_case.interface else ''
                full_url = base_url.rstrip('/') + '/' + path.lstrip('/')

                execution_data = {
                    'case_name': execution.test_case.name,
                    'interface': execution.test_case.interface.name,
                    'method': execution.test_case.interface.method,  # 添加请求方式
                    'url': full_url,  # 添加完整URL
                    'result': execution.get_result_display(),  # 使用显示值
                    'duration': execution.end_time - execution.start_time if execution.end_time and execution.start_time else None,
                    'start_time': execution.start_time.strftime('%Y-%m-%d %H:%M:%S') if execution.start_time else '-',
                    'end_time': execution.end_time.strftime('%Y-%m-%d %H:%M:%S') if execution.end_time else '-',
                    'request': {
                        'url': full_url,
                        'method': execution.test_case.interface.method,
                        'headers': execution.request_data.get('headers', {}) if execution.request_data else {},
                        'params': execution.request_data.get('params', {}) if execution.request_data else {},
                        'data': execution.request_data.get('data', {}) if execution.request_data else {},
                        'json': execution.request_data.get('json', {}) if execution.request_data else {},
                    },
                    'response': {
                        'status_code': execution.response_data.get('status_code', '-') if execution.response_data else '-',
                        'headers': execution.response_data.get('headers', {}) if execution.response_data else {},
                        'body': execution.response_data.get('body', {}) if execution.response_data else {},
                    },
                    'error_message': execution.error_message if execution.error_message else ''
                }
                report_data['executions'].append(execution_data)

            # 生成 PDF 报告
            generator = ReportGenerator(report_data)
            pdf_path = generator.export_pdf('media/reports')

            # 返回 PDF 文件
            from django.http import FileResponse
            response = FileResponse(
                open(pdf_path, 'rb'),
                content_type='application/pdf'
            )
            response['Content-Disposition'] = f'attachment; filename="{report.name}.pdf"'
            return response

        except self.model.DoesNotExist:
            messages.error(request, '报告不存在')
            return redirect('..')
        except Exception as e:
            messages.error(request, f'导出报告时发生错误: {str(e)}')
            return redirect('..')

    def total_cases(self, obj):
        return obj.executions.count()
    total_cases.short_description = '总用例数'

    def passed_cases(self, obj):
        return obj.executions.filter(result='passed').count()
    passed_cases.short_description = '通过用例数'

    def failed_cases(self, obj):
        return obj.executions.filter(result='failed').count()
    failed_cases.short_description = '失败用例数'

    def error_cases(self, obj):
        return obj.executions.filter(result='error').count()
    error_cases.short_description = '错误用例数'

    def skipped_cases(self, obj):
        return obj.executions.filter(result='skipped').count()
    skipped_cases.short_description = '跳过用例数'

    def pass_rate(self, obj):
        total = self.total_cases(obj)
        if total == 0:
            return '0%'
        passed = self.passed_cases(obj)
        return f'{(passed / total * 100):.2f}%'
    pass_rate.short_description = '通过率'

    def view_allure_report(self, request, pk):
        """查看 Allure 报告"""
        try:
            report = self.model.objects.get(pk=pk)
            
            # 生成 Allure 报告
            allure_results_dir = os.path.join(settings.MEDIA_ROOT, 'allure-results')
            allure_report_dir = os.path.join(settings.MEDIA_ROOT, f'allure-reports/{pk}')
            
            # 确保目录存在
            os.makedirs(allure_report_dir, exist_ok=True)
            
            # 检查结果目录是否有文件
            results_files = os.listdir(allure_results_dir)
            if not results_files:
                messages.warning(request, f'Allure 结果目录为空: {allure_results_dir}')
                return redirect('..')
            
            messages.info(request, f'找到 {len(results_files)} 个结果文件')
            
            # 生成报告
            import subprocess
            try:
                # 构建完整命令
                cmd = ['allure', 'generate', allure_results_dir, '-o', allure_report_dir, '--clean']
                messages.info(request, f'执行命令: {" ".join(cmd)}')
                
                # 执行命令并捕获输出
                process = subprocess.run(
                    cmd,
                    check=True,
                    capture_output=True,
                    text=True
                )
                
                # 记录命令输出
                if process.stdout:
                    messages.info(request, f'命令输出: {process.stdout}')
                if process.stderr:
                    messages.warning(request, f'命令错误输出: {process.stderr}')
                
                # 检查报告目录
                if os.path.exists(os.path.join(allure_report_dir, 'index.html')):
                    messages.success(request, 'Allure 报告生成成功')
                    # 返回报告 URL
                    report_url = f'{settings.MEDIA_URL}allure-reports/{pk}/index.html'
                    return redirect(report_url)
                else:
                    messages.error(request, f'报告文件未生成: {allure_report_dir}')
                    return redirect('..')
                
            except subprocess.CalledProcessError as e:
                messages.error(request, f'生成 Allure 报告失败: {str(e)}\n错误输出: {e.stderr}')
                return redirect('..')
            except FileNotFoundError:
                messages.error(request, '未找到 allure 命令。请确保：\n1. 已安装 Allure\n2. Allure 已添加到系统 PATH\n3. 重启了 Django 服务器')
                return redirect('..')
            
        except self.model.DoesNotExist:
            messages.error(request, '报告不存在')
            return redirect('..')
        except Exception as e:
            messages.error(request, f'查看报告时发生错误: {str(e)}')
            return redirect('..')
